<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue 3响应式原理可视化</title>
    <style>
        /* ===== 重置与基础样式 ===== */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f7fa;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        /* ===== 头部样式 ===== */
        header {
            text-align: center;
            margin-bottom: 30px;
        }
        
        h1 {
            color: #42b883;
            margin-bottom: 10px;
        }
        
        .description {
            color: #7f8c8d;
            max-width: 800px;
            margin: 0 auto 20px;
        }
        
        /* ===== 步骤指示器样式 ===== */
        .steps-indicator {
            display: flex;
            justify-content: space-between;
            max-width: 800px;
            margin: 0 auto 30px;
            position: relative;
        }
        
        .steps-indicator::before {
            content: "";
            position: absolute;
            top: 15px;
            left: 0;
            right: 0;
            height: 2px;
            background: #e0e0e0;
            z-index: 1;
        }
        
        .step {
            display: flex;
            flex-direction: column;
            align-items: center;
            position: relative;
            z-index: 2;
        }
        
        .step-circle {
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background: #e0e0e0;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            margin-bottom: 8px;
            transition: all 0.3s;
        }
        
        .step.active .step-circle {
            background: #42b883;
        }
        
        .step.completed .step-circle {
            background: #42b883;
        }
        
        .step.completed .step-circle::after {
            content: "✓";
        }
        
        .step-label {
            font-size: 14px;
            text-align: center;
            max-width: 100px;
        }
        
        /* ===== 可视化区域样式 ===== */
        .visualization-area {
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            padding: 20px;
            margin-bottom: 30px;
            min-height: 500px;
            position: relative;
            overflow: hidden;
        }
        
        /* ===== 控制按钮样式 ===== */
        .controls {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        
        button {
            padding: 10px 20px;
            background-color: #42b883;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
        }
        
        button:hover {
            background-color: #349268;
            transform: translateY(-2px);
        }
        
        button:disabled {
            background-color: #a1d9c3;
            cursor: not-allowed;
            transform: none;
        }
        
        /* ===== 代码区域样式 ===== */
        .code-area {
            background-color: #2d2d2d;
            color: #f8f8f2;
            border-radius: 8px;
            padding: 20px;
            font-family: "Fira Code", monospace;
            overflow-x: auto;
            margin-bottom: 20px;
            position: relative;
        }
        
        .code-title {
            position: absolute;
            top: -10px;
            left: 15px;
            background: #42b883;
            color: white;
            padding: 3px 10px;
            border-radius: 4px;
            font-size: 14px;
        }
        
        /* ===== 说明区域样式 ===== */
        .explanation {
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            padding: 20px;
            margin-top: 20px;
        }
        
        .explanation h3 {
            color: #42b883;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
        }
        
        .explanation h3::before {
            content: "ℹ️";
            margin-right: 8px;
        }
        
        /* ===== 组件样式 ===== */
        .component {
            position: absolute;
            display: flex;
            flex-direction: column;
            align-items: center;
            transition: all 0.5s;
            z-index: 10;
        }
        
        .component-circle {
            width: 80px;
            height: 80px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
            transition: all 0.3s;
        }
        
        .component-label {
            margin-top: 10px;
            font-weight: 500;
            background: white;
            padding: 3px 10px;
            border-radius: 15px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }
        
        /* ===== 动画效果 ===== */
        .data-flow {
            stroke-dasharray: 5;
            animation: dash 1s linear infinite;
        }
        
        @keyframes dash {
            to {
                stroke-dashoffset: -10;
            }
        }
        
        .highlight {
            filter: drop-shadow(0 0 8px #ffeb3b);
            transform: scale(1.05);
        }
        
        .data-value {
            position: absolute;
            background: #ff6b6b;
            color: white;
            padding: 5px 10px;
            border-radius: 20px;
            font-size: 14px;
            display: none;
        }
        
        .value-display {
            position: absolute;
            background: #3498db;
            color: white;
            padding: 8px 15px;
            border-radius: 20px;
            font-size: 16px;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            display: none;
            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
        }
        
        .mini-code {
            font-family: "Fira Code", monospace;
            background: #f1f8ff;
            padding: 2px 6px;
            border-radius: 4px;
            color: #0366d6;
        }
        
        /* ===== 进度条样式 ===== */
        .progress-container {
            height: 8px;
            background: #e0e0e0;
            border-radius: 4px;
            margin: 15px 0;
            overflow: hidden;
        }
        
        .progress-bar {
            height: 100%;
            background: #42b883;
            border-radius: 4px;
            width: 0%;
            transition: width 0.5s;
        }
        
        /* ===== Effect盒子样式 ===== */
        .effect-box {
            position: absolute;
            background: #2ecc71;
            color: white;
            padding: 8px 15px;
            border-radius: 8px;
            font-size: 14px;
            display: none;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Vue 3响应式原理可视化</h1>
            <p class="description">
                本演示展示了Vue 3响应式系统的核心机制，使用Proxy实现数据观察、依赖收集和派发更新。
                按照步骤操作来理解Vue 3如何实现数据驱动视图。
            </p>

            <!-- 步骤指示器 -->
            <div class="steps-indicator">
                <div class="step active" id="step1">
                    <div class="step-circle">1</div>
                    <div class="step-label">初始化数据</div>
                </div>
                <div class="step" id="step2">
                    <div class="step-circle">2</div>
                    <div class="step-label">创建响应式对象</div>
                </div>
                <div class="step" id="step3">
                    <div class="step-circle">3</div>
                    <div class="step-label">创建Effect</div>
                </div>
                <div class="step" id="step4">
                    <div class="step-circle">4</div>
                    <div class="step-label">更新数据</div>
                </div>
            </div>
        </header>

        <!-- 控制按钮 -->
        <div class="controls">
            <button id="initBtn">初始化数据</button>
            <button id="reactiveBtn" disabled>创建响应式对象</button>
            <button id="effectBtn" disabled>创建Effect</button>
            <button id="updateBtn" disabled>更新数据</button>
            <button id="resetBtn">重置演示</button>
            <button id="autoRunBtn">自动运行</button>
        </div>

        <!-- 代码显示区域 -->
        <div class="code-area">
            <div class="code-title">代码演示</div>
            <div id="codeContent">// 代码将根据演示步骤显示在这里</div>
        </div>

        <!-- 进度条 -->
        <div class="progress-container">
            <div class="progress-bar" id="progressBar"></div>
        </div>

        <!-- 可视化区域 -->
        <div class="visualization-area" id="visualization">
            <div class="value-display" id="valueDisplay">当前值:</div>
            <div class="effect-box" id="effectBox">Effect运行中</div>
            <svg id="flowSvg" width="100%" height="500"></svg>
        </div>

        <!-- 说明区域 -->
        <div class="explanation">
            <h3>当前步骤说明</h3>
            <p id="stepExplanation">
                请点击"初始化数据"开始演示，或点击"自动运行"观看完整流程。
            </p>
        </div>
    </div>

    <script>
        // ===== 配置常量 =====
        const componentPositions = {
            data: { x: 100, y: 250 },
            reactive: { x: 350, y: 150 },
            track: { x: 350, y: 350 },
            effect: { x: 600, y: 250 },
            render: { x: 850, y: 250 },
        };

        const componentColors = {
            data: "#3498db",
            reactive: "#9b59b6",
            track: "#e74c3c",
            effect: "#2ecc71",
            render: "#f39c12",
        };

        // ===== 演示状态 =====
        let demoState = {
            data: null,
            reactiveData: null,
            currentEffect: null,
            rendered: false,
            currentStep: 0,
        };

        // ===== 核心功能函数 =====
        
        /**
         * 初始化可视化
         */
        function initVisualization() {
            const visualization = document.getElementById("visualization");
            const svg = document.getElementById("flowSvg");

            // 清空现有内容
            visualization.querySelectorAll(".component").forEach((el) => el.remove());
            svg.innerHTML = "";

            // 创建组件
            createComponent("data", "数据", componentPositions.data, componentColors.data);
            createComponent("reactive", "Reactive", componentPositions.reactive, componentColors.reactive);
            createComponent("track", "Track/Trigger", componentPositions.track, componentColors.track);
            createComponent("effect", "Effect", componentPositions.effect, componentColors.effect);
            createComponent("render", "渲染", componentPositions.render, componentColors.render);

            // 隐藏值显示
            document.getElementById("valueDisplay").style.display = "none";
            document.getElementById("effectBox").style.display = "none";

            // 重置进度条
            document.getElementById("progressBar").style.width = "0%";

            // 重置步骤指示器
            document.querySelectorAll(".step").forEach((step, index) => {
                step.classList.remove("active", "completed");
                if (index === 0) step.classList.add("active");
            });

            // 重置状态
            demoState = {
                data: null,
                reactiveData: null,
                currentEffect: null,
                rendered: false,
                currentStep: 0,
            };

            updateExplanation('点击"初始化数据"开始演示，或点击"自动运行"观看完整流程。');
            showCode("// 准备开始演示Vue 3响应式原理");
        }

        /**
         * 创建组件
         */
        function createComponent(id, label, position, color) {
            const component = document.createElement("div");
            component.id = id;
            component.className = "component";
            component.style.left = position.x + "px";
            component.style.top = position.y + "px";

            const circle = document.createElement("div");
            circle.className = "component-circle";
            circle.style.backgroundColor = color;
            circle.textContent = label.charAt(0);

            const labelEl = document.createElement("div");
            labelEl.className = "component-label";
            labelEl.textContent = label;

            component.appendChild(circle);
            component.appendChild(labelEl);

            document.getElementById("visualization").appendChild(component);
        }

        /**
         * 创建SVG连接线
         */
        function createConnection(from, to, color = "#95a5a6") {
            const svg = document.getElementById("flowSvg");
            const fromEl = document.getElementById(from);
            const toEl = document.getElementById(to);

            if (!fromEl || !toEl) return;

            const fromRect = fromEl.getBoundingClientRect();
            const toRect = toEl.getBoundingClientRect();
            const containerRect = document.getElementById("visualization").getBoundingClientRect();

            const x1 = fromRect.left + fromRect.width / 2 - containerRect.left;
            const y1 = fromRect.top + fromRect.height / 2 - containerRect.top;
            const x2 = toRect.left + toRect.width / 2 - containerRect.left;
            const y2 = toRect.top + toRect.height / 2 - containerRect.top;

            const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
            line.setAttribute("x1", x1);
            line.setAttribute("y1", y1);
            line.setAttribute("x2", x2);
            line.setAttribute("y2", y2);
            line.setAttribute("stroke", color);
            line.setAttribute("stroke-width", "2");
            line.setAttribute("class", "data-flow");
            line.style.opacity = "0";

            svg.appendChild(line);
            return line;
        }

        /**
         * 显示代码
         */
        function showCode(code) {
            document.getElementById("codeContent").innerHTML = code;
        }

        /**
         * 更新说明
         */
        function updateExplanation(text) {
            document.getElementById("stepExplanation").innerHTML = text;
        }

        /**
         * 高亮组件
         */
        function highlightComponent(id, highlight = true) {
            const component = document.getElementById(id);
            if (!component) return;

            const circle = component.querySelector(".component-circle");
            if (highlight) {
                circle.classList.add("highlight");
            } else {
                circle.classList.remove("highlight");
            }
        }

        /**
         * 更新步骤指示器
         */
        function updateStepIndicator(stepIndex, isCompleted = false) {
            document.querySelectorAll(".step").forEach((step, index) => {
                step.classList.remove("active", "completed");
                if (index === stepIndex) {
                    step.classList.add("active");
                } else if (index < stepIndex) {
                    step.classList.add("completed");
                }
            });

            // 更新进度条
            const progress = (stepIndex / 4) * 100;
            document.getElementById("progressBar").style.width = `${progress}%`;
        }

        /**
         * 显示数值
         */
        function showValue(value) {
            const valueDisplay = document.getElementById("valueDisplay");
            valueDisplay.textContent = `当前值: ${value}`;
            valueDisplay.style.display = "block";
        }

        /**
         * 显示Effect
         */
        function showEffect() {
            const effectBox = document.getElementById("effectBox");
            effectBox.style.display = "block";
            effectBox.style.left = componentPositions.effect.x + 100 + "px";
            effectBox.style.top = componentPositions.effect.y + "px";
        }

        /**
         * 动画函数
         */
        function animateConnection(line, duration = 1000) {
            return new Promise((resolve) => {
                line.style.opacity = "1";
                setTimeout(() => {
                    line.style.opacity = "0";
                    resolve();
                }, duration);
            });
        }

        // ===== 事件监听器 =====
        
        // 初始化演示
        document.getElementById("initBtn").addEventListener("click", async function () {
            this.disabled = true;
            document.getElementById("reactiveBtn").disabled = false;

            demoState.currentStep = 1;
            updateStepIndicator(1);

            showCode(`// 初始化响应式数据
const data = {
  message: "Hello Vue 3!"
};`);
            
            updateExplanation(
                '初始化数据对象，这是Vue 3响应式系统的起点。数据对象包含一个<code class="mini-code">message</code>属性，初始值为"Hello Vue 3!"。'
            );

            // 模拟数据创建
            demoState.data = { message: "Hello Vue 3!" };

            // 高亮数据组件
            highlightComponent("data");

            // 显示初始值
            showValue("Hello Vue 3!");

            await new Promise((resolve) => setTimeout(resolve, 1500));
        });

        // 创建响应式对象
        document.getElementById("reactiveBtn").addEventListener("click", async function () {
            this.disabled = true;
            document.getElementById("effectBtn").disabled = false;

            demoState.currentStep = 2;
            updateStepIndicator(2);

            showCode(`// 使用Proxy创建响应式对象
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      track(target, key); // 跟踪依赖
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      trigger(target, key); // 触发更新
      return result;
    }
  });
}

// 依赖跟踪
function track(target, key) {
  if (activeEffect) {
    let depsMap = targetMap.get(target);
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
      depsMap.set(key, (dep = new Set()));
    }
    dep.add(activeEffect);
  }
}`);
            
            updateExplanation(
                '使用<code class="mini-code">Proxy</code>创建响应式对象，Proxy可以拦截对象的各种操作，包括属性访问、赋值、删除等。相比Vue 2的Object.defineProperty，Proxy可以更好地处理数组和对象新增属性。'
            );

            // 高亮Reactive组件
            highlightComponent("reactive");

            // 创建从数据到Reactive的连接线
            const dataToReactive = createConnection("data", "reactive", componentColors.reactive);
            await animateConnection(dataToReactive);

            // 创建从Reactive到Track的连接线
            const reactiveToTrack = createConnection("reactive", "track", componentColors.track);
            await animateConnection(reactiveToTrack);

            highlightComponent("reactive", false);
            highlightComponent("track");

            await new Promise((resolve) => setTimeout(resolve, 1000));
        });

        // 创建Effect
        document.getElementById("effectBtn").addEventListener("click", async function () {
            this.disabled = true;
            document.getElementById("updateBtn").disabled = false;

            demoState.currentStep = 3;
            updateStepIndicator(3);

            showCode(`// 创建Effect
let activeEffect = null;
const targetMap = new WeakMap();

function effect(fn) {
  activeEffect = fn;
  fn(); // 执行函数，触发getter收集依赖
  activeEffect = null;
}

// 在组件中使用
effect(() => {
  // 这个函数会立即执行
  console.log('Effect运行:', reactiveData.message);
  // 更新视图...
  renderComponent();
});`);
            
            updateExplanation(
                "创建Effect，Effect是一个副作用函数，当它内部使用的响应式数据发生变化时，Effect会自动重新执行。Vue 3的组件渲染和计算属性都是基于Effect实现的。"
            );

            // 高亮Effect组件
            highlightComponent("effect");

            // 创建从Track到Effect的连接线
            const trackToEffect = createConnection("track", "effect", componentColors.effect);
            await animateConnection(trackToEffect);

            // 创建从Effect到渲染的连接线
            const effectToRender = createConnection("effect", "render", componentColors.render);
            await animateConnection(effectToRender);

            highlightComponent("track", false);
            highlightComponent("render");

            // 显示Effect
            showEffect();

            // 模拟初始渲染
            demoState.rendered = true;
            updateExplanation(
                '初始Effect已运行，页面现在显示数据：<code class="mini-code">' +
                demoState.data.message +
                "</code>"
            );

            await new Promise((resolve) => setTimeout(resolve, 1500));
            highlightComponent("effect", false);
            highlightComponent("render", false);
        });

        // 更新数据
        document.getElementById("updateBtn").addEventListener("click", async function () {
            this.disabled = true;

            demoState.currentStep = 4;
            updateStepIndicator(4, true);

            showCode(`// 设置新值，触发Proxy setter
reactiveData.message = "Hello Reactive System!";

// setter中的逻辑
function setter(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  trigger(target, key); // 触发所有相关的Effect
  return result;
}

// 触发更新
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  
  const dep = depsMap.get(key);
  if (dep) {
    dep.forEach(effect => effect()); // 重新执行所有Effect
  }
}`);
            
            updateExplanation(
                '修改响应式数据属性，触发Proxy setter，setter调用<code class="mini-code">trigger()</code>，trigger找到所有相关的Effect并重新执行它们，从而实现自动更新。'
            );

            // 高亮数据组件
            highlightComponent("data");

            // 模拟数据更新
            demoState.data.message = "Hello Reactive System!";
            showValue("Hello Reactive System!");

            // 创建从数据到Reactive的连接线
            const dataToReactive = createConnection("data", "reactive", componentColors.reactive);
            await animateConnection(dataToReactive);

            highlightComponent("data", false);
            highlightComponent("reactive");

            // 创建从Reactive到Track的连接线
            const reactiveToTrack = createConnection("reactive", "track", componentColors.track);
            await animateConnection(reactiveToTrack);

            highlightComponent("reactive", false);
            highlightComponent("track");

            // 创建从Track到Effect的连接线
            const trackToEffect = createConnection("track", "effect", componentColors.effect);
            await animateConnection(trackToEffect);

            highlightComponent("track", false);
            highlightComponent("effect");

            // 创建从Effect到渲染的连接线
            const effectToRender = createConnection("effect", "render", componentColors.render);
            await animateConnection(effectToRender);

            highlightComponent("effect", false);
            highlightComponent("render");

            // 更新渲染内容
            updateExplanation(
                '更新完成，页面现在显示新数据：<code class="mini-code">' +
                demoState.data.message +
                "</code>。这就是Vue 3响应式系统的完整工作流程，基于Proxy和Effect实现。"
            );

            await new Promise((resolve) => setTimeout(resolve, 1500));
            highlightComponent("render", false);
        });

        // 重置演示
        document.getElementById("resetBtn").addEventListener("click", function () {
            document.getElementById("initBtn").disabled = false;
            document.getElementById("reactiveBtn").disabled = true;
            document.getElementById("effectBtn").disabled = true;
            document.getElementById("updateBtn").disabled = true;

            initVisualization();
        });

        // 自动运行
        document.getElementById("autoRunBtn").addEventListener("click", async function () {
            // 禁用所有按钮
            document.getElementById("initBtn").disabled = true;
            document.getElementById("reactiveBtn").disabled = true;
            document.getElementById("effectBtn").disabled = true;
            document.getElementById("updateBtn").disabled = true;
            document.getElementById("resetBtn").disabled = true;
            this.disabled = true;

            // 重置演示
            initVisualization();
            await new Promise((resolve) => setTimeout(resolve, 1000));

            // 按顺序触发每个步骤
            document.getElementById("initBtn").click();
            await new Promise((resolve) => setTimeout(resolve, 2000));

            document.getElementById("reactiveBtn").click();
            await new Promise((resolve) => setTimeout(resolve, 2500));

            document.getElementById("effectBtn").click();
            await new Promise((resolve) => setTimeout(resolve, 2500));

            document.getElementById("updateBtn").click();
            await new Promise((resolve) => setTimeout(resolve, 2500));

            // 重新启用按钮
            document.getElementById("resetBtn").disabled = false;
            this.disabled = false;
        });

        // ===== 初始化页面 =====
        window.onload = initVisualization;
    </script>
</body>
</html>